package org.leumasjaffe.math.probability;

import java.util.Random;

import org.eclipse.jdt.annotation.NonNull;
import org.leumasjaffe.util.Pair;

import flanagan.analysis.Stat;

public class NormalRV extends AbstractRV {
	private final double mu;
	private final double sigma;
	
	public static final NormalRV standard = new NormalRV() {
		@Override
		public Pair<Double, Double> boxMuller() {
			UniformRV urv = new UniformRV(0.0, 1.0);
			double u = urv.getX();
			double v = urv.getX();
			return new Pair<Double, Double>(Math.sqrt(-2*Math.log(u))*Math.cos(Math.PI*2*v),
					Math.sqrt(-2*Math.log(u))*Math.sin(Math.PI*2*v));
		}
	};
	
	public NormalRV() {
		this.mu = 0;
		this.sigma = 1;
	}
	
	public NormalRV(double mu, double sigma) {
		this.mu = mu;
		this.sigma = sigma;
	}
	
	public NormalRV(@NonNull Random r) {
		super(r);
		this.mu = 0;
		this.sigma = 1;
	}
	
	public NormalRV(@NonNull Random r, double mu, double sigma) {
		super(r);
		this.mu = mu;
		this.sigma = sigma;
	}

	@Override
	public double getX() {
		double u = getRand();
		return this.sigma*calcX(u)+this.mu;
	}
	
	@Override
	public double calcX(double p) {
		if (!isProb(p)) throw new IllegalArgumentException("Arguement is not a valid probability");
		return Stat.normalInverseCDF(this.mu, this.sigma, p);
	}

	@Override
	public double getEV() {
		return this.mu;
	}
	
	@Override
	public double getVar() {
		return this.sigma*this.sigma;
	}

	@Override
	public double getP(double x) {
		return 1/(Math.sqrt(2*Math.PI)*this.sigma)*Math.exp(((x-this.mu)*(x-this.mu))/(2*this.sigma*this.sigma));
	}

	@Override
	public double getF(double x) {
		return 0.5*(1+Stat.erf((x-this.mu)/(Math.sqrt(2.0)*this.sigma)));
	}
	
	public Pair<Double, Double> boxMuller() {
		throw new AssertionError("Only use this function with the standard Normal provided");
	}

}
